﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Reflection;
using System.IO;
using System.Drawing;
using System.ComponentModel;
using BF = System.Reflection.BindingFlags;

namespace GestionRecaudos
{
    /// <remarks>
    /// Esta calse tiene la responsabilidad de asegurar el acceso a los formualrios de la aplicacion, 
    /// ademas de permitir la navegacion entre estos.
    /// </remarks>
    public class ManageForms
    {
        public static string NameSpace = "GestionRecaudos.";
        /// <summary>
        /// lista de formularios registrados.
        /// </summary>
        private static List<ManageForms_ItemList> formulariosInParent = new List<ManageForms_ItemList>();
        /// <summary>
        /// Formulario principal del maestro (Debe ser el primero en ser registrado).
        /// </summary>
        private static Form mdiContainer = null;


        public static bool addNewForm(Form form)
        {
            return ManageForms.addNewForm(form, false, false, false);
        }
        public static bool addNewForm(Form form, bool cerrarFormulario)
        {
            return ManageForms.addNewForm(form, cerrarFormulario, false, false);
        }
        /// <summary>
        /// Agregar un nuevo formulario a la lista.
        /// </summary>
        /// <param name="form">Formulario a agregar</param>
        /// <param name="cerrarFormulario">Eliminar el formulario de la lista cuando el usuario lo cierre</param>
        /// <param name="isEmergente">Asociar el formulario al MDI</param>
        /// <param name="MostrarMSN">mostrar mensajes cuando se presente una excepcion durante la carga del formualrio</param>
        /// <returns></returns>
        public static bool addNewForm(Form form, bool cerrarFormulario, bool isEmergente, Boolean MostrarMSN)
        {
            if (form.IsMdiContainer && ManageForms.MdiContainer == null)
            {
                ManageForms.mdiContainer = form;
                ManageForms.formulariosInParent.Add(new ManageForms_ItemList(form, cerrarFormulario, isEmergente));
                return true;
            }
            else if (form.IsMdiContainer && ManageForms.MdiContainer != null)
            {
                if (MostrarMSN)
                {
                    MessageBox.Show("Ya existe un Form.MdiContainer.\nEl formulario no puede ser agregado.");
                }
                return false;
            }
            else if (ManageForms.MdiContainer == null)
            {
                if (MostrarMSN)
                {
                    MessageBox.Show("Aun no se ha definido un Form.MdiContainer.\nEl formulario no puede ser agregado.");
                }
                return false;
            }
            else
            {
                //--verificar si ya el formulario esta registrado en la lista.
                foreach (ManageForms_ItemList f in ManageForms.formulariosInParent)
                {
                    //--- si esta registrado... no tengo que hacer mas nada
                    if (f.Formulario.GetType().ToString().Equals(form.GetType().ToString()))
                    {
                        if (MostrarMSN)
                        {
                            MessageBox.Show("El formulario " + form.GetType().ToString() + " Ya esta registrado en la lista de Formularios.");
                        }
                        return true;
                    }
                }
                //--asignarle el MdiContainer, solo si NO es una ventana emergente
                if (!isEmergente)
                {
                    form.MdiParent = ManageForms.MdiContainer;
                }
                //--agregar el formulario a la lista, SOLO SI el parametro cerrarFormulario=false
                if (!cerrarFormulario)
                {
                    ManageForms.formulariosInParent.Add(new ManageForms_ItemList(form, cerrarFormulario, isEmergente));
                }
                return true;
            }
        }


        public static bool addNewForm(String FullFormName)
        {
            return ManageForms.addNewForm(FullFormName, false, false, false);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullFormName">Nombre del formulario</param>
        /// <param name="cerrarFormulario">Eliminar el formulario de la lista cuando el usuario lo cierre</param>
        /// <returns></returns>
        public static bool addNewForm(String FullFormName, bool cerrarFormulario)
        {
            return ManageForms.addNewForm(FullFormName, cerrarFormulario, false, false);
        }
        public static bool addNewForm(String FullFormName, bool cerrarFormulario, bool isEmergente, Boolean MostrarMSN)
        {
            Boolean ok = false;
            Form temp = null;
            // try
            // {
            //--- verificio si el formulario ya fue creado...
            foreach (ManageForms_ItemList f in ManageForms.formulariosInParent)
            {
                if (f.Formulario.GetType().ToString().Equals(FullFormName))
                {
                    ok = true;
                    temp = f.Formulario;
                }
            }

            //--- si no fue creado,lo creo
            if (!ok)
            {
                temp = instanciar(FullFormName, cerrarFormulario);
            }
            return addNewForm(temp, cerrarFormulario, isEmergente, MostrarMSN);
            //}
            //catch (Exception)
            //{
            //    //AWControls.VentanasEmergentes.Show(mensaje: "NO fue posible cargar el formulario.\nVerifique la existencia del formulario " + FullFormName, titulo: "Error.", icono: System.Windows.Forms.MessageBoxIcon.Error);
            //    MessageBox.Show("NO fue posible cargar el formulario.\nVerifique la existencia del formulario " + FullFormName);
            //    return false;
            //}
        }


        public static void open(String FullFormName)
        {
            ManageForms.open(FullFormName, false, false, false);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullFormName">Nombre del formulario</param>
        /// <param name="cerrarFormulario">Eliminar el formulario de la lista cuando el usuario lo cierre</param>
        public static void open(String FullFormName, bool cerrarFormulario)
        {
            ManageForms.open(FullFormName, cerrarFormulario, false, false);
        }
        public static void open(String FullFormName, bool cerrarFormulario, bool esEmergente, Boolean MostrarMSN)
        {
            Boolean ok = false;
            Form temp = null;
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                //--- verificio si el formulario ya fue creado...
                foreach (ManageForms_ItemList f in ManageForms.formulariosInParent)
                {
                    if (f.Formulario.GetType().ToString().Equals(FullFormName))
                    {
                        ok = true;
                        temp = f.Formulario;
                        break;
                    }
                    //-- Es posible que el metodo unicamente reciba el nombre del formulario (sin colocar los NameSpace), para evitar que el programador deba colocar los nameSpace cada vez que desee invocar un formulario
                    //-- verifico si el formulario (Unicamente el noombre) esta en la lista
                    else if (!FullFormName.Contains('.') && f.Formulario.GetType().ToString().Length > FullFormName.Length)
                    {
                        if (f.Formulario.GetType().ToString().Substring((f.Formulario.GetType().ToString().Length - FullFormName.Length), FullFormName.Length).Equals(FullFormName))
                        {
                            ok = true;
                            temp = f.Formulario;
                            break;
                        }
                    }
                }
                //--- si ya fue creado, entonces lo muestro
                if (ok)
                {
                    if (temp.Visible)
                    {
                        temp.BringToFront();
                        temp.WindowState = (temp.WindowState == FormWindowState.Minimized ? FormWindowState.Normal : temp.WindowState);
                        Cursor.Current = Cursors.Default;
                        return;
                    }
                    else
                    {
                        ManageForms.reloadForm(temp);
                        temp.Show();
                        temp.WindowState = (temp.WindowState == FormWindowState.Minimized ? FormWindowState.Normal : temp.WindowState);
                        Cursor.Current = Cursors.Default;
                        return;
                    }
                }
                //--- si no fue creado,lo creo, lo registro y lo muestro
                temp = instanciar(FullFormName, cerrarFormulario);
                ManageForms.addNewForm(temp, false, false, false);
                if (esEmergente)
                {
                    temp.ShowDialog();
                    Cursor.Current = Cursors.Default;
                    return;
                }
                else
                {
                    temp.MdiParent = ManageForms.MdiContainer;
                    temp.Show();
                    Cursor.Current = Cursors.Default;
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageException.show(ex);
            }
            Cursor.Current = Cursors.Default;
        }


        public static Form getFormulario(String FullFormName)
        {
            return ManageForms.getFormulario(FullFormName, false, false, false);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullFormName">Nombre del formulario</param>
        /// <param name="cerrarFormulario">Eliminar el formulario de la lista cuando el usuario lo cierre</param>
        public static Form getFormulario(String FullFormName, bool cerrarFormulario)
        {
            return ManageForms.getFormulario(FullFormName, cerrarFormulario, false, false);
        }
        /// <summary>
        /// Obtener un formulario de la lista. Si no esta registrado, lo instancia y lo registra.
        /// </summary>
        /// <param name="nombreFormulario">Nombre del formulario</param>
        /// <returns>Instancia del formulario</returns>
        public static Form getFormulario(String FullFormName, bool cerrarFormulario, bool esEmergente, Boolean MostrarMSN)
        {
            foreach (ManageForms_ItemList f in ManageForms.formulariosInParent)
            {
                if (f.Formulario.GetType().ToString().Equals(FullFormName))
                {
                    return f.Formulario;
                }
                else if (!FullFormName.Contains('.') && f.Formulario.GetType().ToString().Length > FullFormName.Length)
                {
                    if (f.Formulario.GetType().ToString().Substring((f.Formulario.GetType().ToString().Length - FullFormName.Length), FullFormName.Length).Equals(FullFormName))
                    {
                        return f.Formulario;
                    }
                }
            }
            Cursor.Current = Cursors.WaitCursor;
            Form temp = instanciar(FullFormName, cerrarFormulario);
            ManageForms.addNewForm(temp, cerrarFormulario, esEmergente, MostrarMSN);
            Cursor.Current = Cursors.Default;
            return temp;
        }

        private static Form instanciar(string FullFormName, bool cerrarFormulario)
        {
            try
            {
                string path = Application.StartupPath;
                Assembly ensamblado = null;
                ensamblado = System.Reflection.Assembly.LoadFrom(Application.ExecutablePath);

                Type tipos;
                tipos = ensamblado.GetType((FullFormName.Contains(ManageForms.NameSpace)) ? FullFormName : ManageForms.NameSpace + FullFormName);
                Form temp = ((Form)(Activator.CreateInstance(tipos, true)));

                //-- seteo las propiedades basicas de los formularios
                temp.WindowState = ((Utils.get().getValorParametro("MaximizarFormulariosDeLaAplicacion").Equals("SI") ? true : false) ? FormWindowState.Maximized : temp.WindowState);
                temp.BackColor = Color.White;
                temp.ShowIcon = false;
                temp.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
                temp.Text = Utils.get().getValorParametro("NombreEmpresa");

                //--agregar el FormClosing() al formulario, esto es para controlar el cierre del formulario.
                if (!cerrarFormulario)
                {
                    temp.FormClosing += new System.Windows.Forms.FormClosingEventHandler(ManageForms.FormClosing);
                }
                //-- Si el formulario contiene algun Control DataGridView, controlo el Evento dataGridView_DataError
                Utils.get().controlarDataErrorEnTodosLosDataGridView(temp);

                return temp;
            }
            catch (Exception)
            {
                return null;
            }
        }
        /// <summary>
        /// Cerrar todos los formularios abiertos. Este metodo finaliza la aplicación.
        /// </summary>
        public static void closeAllForms()
        {
            Environment.Exit(0);
        }
        /// <summary>
        /// Obtiene el formulario principal de la aplicacion.
        /// </summary>
        public static Form MdiContainer
        {
            get { return ManageForms.mdiContainer; }
            //set { ManageForms.mdiContainer = value; }
        }
        /// <summary>
        /// en vez de cerrar un formulario, lo oculto.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public static void FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                // ManageForms_ItemList borrar = null;

                foreach (ManageForms_ItemList f in ManageForms.formulariosInParent)
                {
                    if (f.Formulario.GetType().ToString().Equals(((Form)sender).GetType().ToString()))
                    {
                        if (f.Formulario.IsMdiChild && !f.CerrarFormulario)
                        {
                            e.Cancel = true;
                            ((Form)sender).Hide();
                            return;
                        }
                        else if (f.Formulario.IsMdiChild && f.CerrarFormulario)
                        {
                            //borrar=f;
                            ManageForms.formulariosInParent.Remove(f);
                            return;
                        }
                    }
                }
                //ManageForms.formulariosInParent.Remove(borrar);
            }
            catch (Exception ex)
            {
                MessageException.show(ex);
            }


        }

        

        #region Con los metodos contenido en esta region logro invocar el metodo handle Load de cada formulario cada vez que se muetre
        
        /// <summary>
        /// Con este metodo invoco el metodo Load (Sí lo tiene definido) del formulario cada vez que este se muestre "form.show()"...
        /// </summary>
        /// <param name="form">Formulario</param>
        /// <returns></returns>
        public static bool reloadForm(Form form)
        {
            ///--- Referencias:
            /// http://www.codeproject.com/Questions/72040/how-to-get-method-name-of-control-event-handler
            /// http://www.codeproject.com/Articles/308536/How-to-copy-event-handlers-from-one-control-to-ano
            /// http://www.binrand.com/post/1540612-bindingflags-delegate-c-detatching-all-event-handlers-closed-c-click-new.html
            /// http://msdn.microsoft.com/en-us/library/a89hcwhh.aspx
            try
            {

                EventHandlerList ctrlEventsCollection = (EventHandlerList)typeof(Control).GetProperty("Events", BF.GetProperty | BF.NonPublic | BF.Instance).GetValue(form, null);
                var headInfo = typeof(EventHandlerList).GetField("head", BF.Instance | BF.NonPublic);
                Dictionary<object, Delegate[]> handlers = BuildList(headInfo, ctrlEventsCollection);

                foreach (KeyValuePair<object, Delegate[]> pair in handlers)
                {
                    for (int x = 0; x < pair.Value.Length; x++)
                    {
                        if (pair.Value[x].Method.Name.EndsWith("_Load"))
                        {
                            pair.Value[x].Method.Invoke(form, new object[] { null, null });
                            return true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageException.show(ex);
                return false;
            }
            return true;
        }
        private static Dictionary<object, Delegate[]> BuildList(FieldInfo headInfo, object eventHandlersList)
        {
            var handlers = new Dictionary<object, Delegate[]>();
            object head = headInfo.GetValue(eventHandlersList);
            if (head != null)
            {
                Type listEntryType = head.GetType();
                FieldInfo delegateInfo = listEntryType.GetField("handler", BF.Instance | BF.NonPublic);
                FieldInfo currentKeyInfo = listEntryType.GetField("key", BF.Instance | BF.NonPublic);
                FieldInfo nextKeyInfo = listEntryType.GetField("next", BF.Instance | BF.NonPublic);
                handlers = BuildListWalk(handlers, head, delegateInfo, currentKeyInfo, nextKeyInfo);
            }
            return handlers;
        }
        private static Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict, object entry, FieldInfo delegateInfo, FieldInfo keyInfo, FieldInfo nextKeyInfo)
        {
            if (entry != null)
            {
                var del = (Delegate)delegateInfo.GetValue(entry);
                object key = keyInfo.GetValue(entry);
                object next = nextKeyInfo.GetValue(entry);

                if (del != null)
                {
                    Delegate[] listeners = del.GetInvocationList();
                    if (listeners.Length > 0)
                    {
                        dict.Add(key, listeners);
                    }
                }
                if (next != null)
                {
                    dict = BuildListWalk(dict, next, delegateInfo, keyInfo, nextKeyInfo);
                }
            }
            return dict;
        }

        #endregion
    }
}
